#include "netlib/net/poller/poll_poller.h"

#include <sys/poll.h>

#include "netlib/base/logger.h"
#include "netlib/net/event/channel.h"
#include "netlib/net/event/event_loop.h"

using namespace netlib;

Timestamp netlib::net::PollPoller::Poll(int timeout, ChannelVector* activate_channels) {
	int n_events = ::poll(pollfds_.data(), pollfds_.size(), timeout);
	Timestamp now(Timestamp::Now());
	if (n_events > 0) {
		LOG_TRACE << n_events << " events happened";
		FillActivateChannels(n_events, activate_channels);
	} else if (n_events < 0) {
		LOG_SYSERR << "Poller::poll()";
	} else {
		LOG_TRACE << "nothing happended";
	}
	return now;
}

void netlib::net::PollPoller::FillActivateChannels(int n_events,
                                                   ChannelVector* activate_channels) const {
	for (auto f_it = pollfds_.begin(); f_it != pollfds_.end() && n_events > 0; ++f_it) {
		if (f_it->revents > 0) {
			--n_events;
			auto c_it = channels_.find(f_it->fd);
			assert(c_it != channels_.end());
			assert(c_it->second->Fd() == f_it->fd);
			c_it->second->SetRevents(f_it->revents);
			activate_channels->push_back(c_it->second);
		}
	}
}

void netlib::net::PollPoller::AddChannel(Channel* channel) {
	assert(channels_.find(channel->Fd()) == channels_.end());
	struct pollfd fd {
		channel->Fd(), static_cast<int16_t>(static_cast<int16_t>(channel->Events())), 0
	};
	pollfds_.push_back(fd);
	channel->SetIndex(static_cast<int>(pollfds_.size() - 1));
	channels_.emplace(channel->Fd(), channel);
}

void netlib::net::PollPoller::ResetChannel(Channel* channel) {
	assert(channels_.find(channel->Fd()) != channels_.end());
	assert(channels_.at(channel->Fd()) == channel);
	int idx = channel->Index();
	assert(idx >= 0 && ImplicitCast<size_t>(idx) < pollfds_.size());
	assert(channel->Fd() == pollfds_[idx].fd);
	pollfds_[idx].revents = 0;
	if (channel->IsNoneEvent()) {
		pollfds_[idx].events = -1;
	} else {
		pollfds_[idx].events = static_cast<int16_t>(channel->Events());
	}
}

void netlib::net::PollPoller::RemovePollfd(Channel* channel) {
	int idx = channel->Index();
	assert(idx >= 0 && ImplicitCast<size_t>(idx) < pollfds_.size());
	if (ImplicitCast<size_t>(idx) != pollfds_.size() - 1) {
		int endfd = pollfds_.back().fd;
		std::iter_swap(pollfds_.begin() + idx, pollfds_.end() - 1);
		assert(endfd > 0);
		channels_.at(endfd)->SetIndex(idx);
	}
	pollfds_.pop_back();
}
